<?php

namespace App\Services;

use App\Models\User as UserModel;
use App\Models\Role as RoleModel;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use \Illuminate\Support\Facades\Redis;

/**
 * Class User   后台用户业务类
 * @package App\Service
 */
class User extends BaseService
{
    /**
     * UserService constructor.
     */
    public function __construct()
    {
        $this->model = new UserModel();
    }

    /**
     * login 登录业务
     * @param array $data
     * @return bool
     * @throws \Exception
     */
    public function login(array $data)
    {
        // 安全ip校验
        call_user_func([$this, 'checkErrorLoginIp']);
        // 判断超过错误登录次数
        $credentials['username'] = $data['username'];
        $credentials['password'] = $data['password'];
        $result = Auth::guard('web')->attempt($credentials);
        // 账号或密码错误
        if (!$result) {
            // 记录错误日志
            call_user_func([$this, 'recordErrorLogin']);

            throw new \Exception('账号或密码错误！');
        }
        // 用户状态校验
        call_user_func([$this, 'checkUserState']);
        DB::beginTransaction();
        try {
            // 更新登录状态
            call_user_func([$this, 'updateLoginState'], [
                'last_login_ip' => request()->ip(),
                'last_login_time' => Carbon::now(),
                'login_count' => DB::raw('login_count + 1'),
            ]);
            // 触发登录成功事件(记录日志)
            // event(new \App\Events\AdminUserLogin(UserModel::find(request()->user()->id)));
        } catch (\Exception $exception) {
            DB::rollBack();

            throw new \Exception($exception->getMessage());
        }
        DB::commit();

        return $result;
    }

    /**
     * recordErrorLogin 记录错误登录ip
     * @return bool
     */
    public function recordErrorLogin(): bool
    {
        $key = 'error_login_ip:' . request()->ip();
        if (Redis::get($key) && Redis::get($key) < 10) {
            Redis::incr($key, 1);
        } else {
            Redis::setex($key, 3600, 1);
        }

        return true;
    }

    /**
     * checkErrorLoginIp ip错误登录校验
     * @return bool
     * @throws \Exception
     */
    private function checkErrorLoginIp(): bool
    {
        $key = 'error_login_ip:' . request()->ip();
        if (!empty(Redis::get($key)) && Redis::get($key) >= 10) {
            throw new \Exception('连续输入错误已达10次，为了保证账户安全，系统已经禁止您操作1小时!');
        }
        return true;
    }

    /**
     * checkUserState 检测用户的状态
     * @return bool
     * @throws \Exception
     */
    private function checkUserState(): bool
    {
        $status = $this->model->where('id', auth('web')->user()->id)->value('status');
        if ($status != 1) {
            // 注销登录
            Auth::guard('web')->logout();
            throw new \Exception('用户已被禁用，请联系后台管理员');
        }

        return true;
    }

    /**
     * updateLoginState 更新登录成功的状态
     * @param array $data
     * @return bool
     */
    private function updateLoginState(array $data): bool
    {
        $result = $this->model->where('id', auth('web')->user()->id)->update($data);
        if ($result) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * logout 注销登录
     * @return bool
     * @throws \Exception
     */
    public function logout(): bool
    {
        try {
            Auth::guard('web')->logout();
        } catch (\Exception $exception) {
            throw new \Exception($exception->getMessage());
        }

        return true;
    }

    /**
     * getUserMenus 用户拥有的所有菜单
     * @return array
     */
    public function getUserMenus()
    {
        // 用户所属的角色id
        $role_ids = $this->getUserRoleIds();
        if (empty($role_ids)) {
            return [];
        }
        // 角色组拥有的所有菜单id
        $menu_ids = $this->getMenuIdsByRoleIds($role_ids);
        if (empty($menu_ids)) {
            return [];
        }
        // 获取所有的菜单及其父级菜单
        return $this->getMenusByMenuIds($menu_ids);
    }

    /**
     * getUserRoleIds 获取用户所属的所有角色id
     * @return array
     */
    public function getUserRoleIds()
    {
        return \App\Models\User::find(auth('web')->user()->id)->roles()->pluck('id')->toArray();
    }

    /**
     * getMenuIdsByRoleIds 获取角色组拥有的所有菜单id
     * @param array $role_ids
     * @return array
     */
    public function getMenuIdsByRoleIds(array $role_ids)
    {
        $menus = [];
        foreach ($role_ids as $key => $value) {
            $menu = \App\Models\Role::find($role_ids[$key])->menus()->get()->toArray();
            if (empty($menus)) {
                $menus = $menu;
            } else {
                $menus = array_merge($menus, $menu);
            }
        }
        if (!empty($menus)) {
            // 过滤重复的数组
            foreach ($menus as $key => $value) {
                if (isset($out[$value['id']])) {
                    unset($menus[$key]);
                } else {
                    $out[$value['id']] = $value;
                }
            }
        }

        return array_column($menus, 'id');
    }

    /**
     * getMenusByMenuIds 获取菜单及其父级菜单
     * @param array $menu_ids
     * @return array
     */
    public function getMenusByMenuIds(array $menu_ids)
    {
        $auth_menus = \App\Models\Menu::whereIn('id', $menu_ids)->get()->toArray();
        $parent_menus = [];
        foreach ($menu_ids as $menu_id) {
            $menu = \App\Models\Menu::find($menu_id)->ancestors->toArray();
            if (empty($parent_menus)) {
                $parent_menus = $menu;
            } else {
                $parent_menus = array_merge($parent_menus, $menu);
            }
        }
        if (!empty($parent_menus)) {
            // 过滤重复的数组
            foreach ($parent_menus as $key => $value) {
                if (isset($out[$value['id']])) {
                    unset($parent_menus[$key]);
                } else {
                    $out[$value['id']] = $value;
                }
            }
        }
        $menus = array_merge($auth_menus, array_values($parent_menus));
        // 菜单树
        if (!empty($menus)) {
            foreach ($menus as $key => $value) {
                // 去除按钮
                if ($menus[$key]['type'] == 2) {
                    unset($menus[$key]);
                } else {
                    $menus[$key]['meta'] = [
                        'title' => $menus[$key]['title'],
                        'icon' => $menus[$key]['icon'],
                        'noCache' => $menus[$key]['noCache'] == 1 ? true : false,
                        'breadcrumb' => $menus[$key]['breadcrumb'] == 1 ? true : false,
                        'affix' => $menus[$key]['affix'] == 1 ? true : false,
                        'activeMenu' => $menus[$key]['activeMenu'] != null ? $menus[$key]['activeMenu'] : ''
                    ];
                }
            }
            $result = getTree(array_values($menus));
        } else {
            $result = [];
        }

        return $result;
    }

    /**
     * getDetailById 用户详情
     * @param int $id
     * @return array
     * @throws \Exception
     */
    public function getDetailById(int $id): array
    {
        $info = parent::getDetailById($id); // TODO: Change the autogenerated stub
        $info['roles'] = \App\Models\User::find($id)->roles()->pluck('role_id');

        return $info;
    }

    /**
     * insertData 新增用户
     * @param array $data
     * @return bool
     * @throws \Exception
     */
    public function insertData(array $data): bool
    {
        // 角色id
        $role_ids = $data['roles'];
        // 密码执行加密
        if (!empty($data['password'])) {
            $data['password'] = Hash::make($data['password']);
        }
        DB::beginTransaction();
        // 新增用户
        try {
            // 过滤非数据表字段
            $data = call_user_func([$this, 'filterFields'], $data);
            // 新增数据
            $this->model->fill($data)->save();
            // 角色-用户关系新增
            if (!empty($role_ids)) {
                foreach ($role_ids as $role_id) {
                    if (empty(RoleModel::find($role_id))) {
                        DB::rollBack();

                        throw new \Exception('角色id为' . $role_id . '不存在');
                    }
                }
                UserModel::find($this->model->id)->roles()->attach($role_ids);
            }
        } catch (\Exception $exception) {
            DB::rollBack();

            throw new \Exception($exception->getMessage());
        }
        DB::commit();

        return true;
    }

    /**
     * updateData 更新用户
     * @param int $id
     * @param array $data
     * @return bool
     * @throws \Exception
     */
    public function updateData(int $id, array $data): bool
    {
        // 角色id
        $role_ids = $data['roles'];
        // 密码执行加密
        if (!empty($data['password'])) {
            $data['password'] = Hash::make($data['password']);
        }
        DB::beginTransaction();
        try {
            $data = call_user_func([$this, 'filterFields'], $data);
            // 更新数据
            $this->model->where('id', $id)->update($data);
            // 角色-用户关系更新
            if (!empty($role_ids)) {
                foreach ($role_ids as $role_id) {
                    if (empty(RoleModel::find($role_id))) {
                        DB::rollBack();

                        throw new \Exception('角色id为' . $role_id . '不存在');
                    }
                }
            }
            UserModel::find($id)->roles()->sync($role_ids);
        } catch (\Exception $exception) {
            DB::rollBack();

            throw new \Exception($exception->getMessage());
        }
        DB::commit();

        return true;
    }
}
